TODO:

library(rtweet)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ------------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v purrr   0.3.4
v tibble  3.1.6     v dplyr   1.0.8
v tidyr   1.2.0     v stringr 1.4.0
v readr   2.1.2     v forcats 0.5.1
-- Conflicts ---------------------------------------- tidyverse_conflicts() --
x dplyr::filter()  masks stats::filter()
x purrr::flatten() masks rtweet::flatten()
x dplyr::lag()     masks stats::lag()
library(keyring)
library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
# twitter_token <- create_token(
#   app = "pacto-social-1",
#   consumer_key = 
#     keyring::key_get("pacto-social-1-consumer_key"),
#   consumer_secret = 
#     keyring::key_get("pacto-social-1-consumer_secret"),
#   access_token = 
#     keyring::key_get("pacto-social-1-access_token"),
#   access_secret =
#     keyring::key_get("pacto-social-1-access_secret"),
#   set_renv = TRUE)
# Retrieving tweets from Gabriel Boric
tweets_by_boric <-
  xfun::cache_rds({
    get_timeline("@gabrielboric", n = 3200)
  },
  file = "tweets_by_boric.rds")

# Retrieving tweets from Jose Antonio Kast
tweets_by_kast <- xfun::cache_rds({
  get_timeline("@joseantoniokast", n = 3200)
  },
  file = "tweets_by_kast.rds")
# Function to filter organic tweets and rank them by engagement
get_organic_ranked <- function(df) {
  df %>%
    filter(is_retweet == FALSE,
           is.na(reply_to_status_id),
           # only tweets previous to the presidential election
           created_at <= lubridate::ymd(20211220)) %>%
    mutate(rank_engagement = dense_rank(desc(favorite_count + retweet_count))) %>%
    arrange(rank_engagement)
}

Then I apply the function to both dataframes and merge the results with map_df. Next, I filter the result to keep only the 10 more popular tweets of each candidate, and select the relevant columns for the visualization.

tweets_for_plot <-
  list(tweets_by_boric,
       tweets_by_kast) %>%
  map_df(get_organic_ranked) %>%
  filter(rank_engagement <= 10) %>%
  transmute(
    rank_engagement,
    screen_name,
    status_id,
    created_at,
    text,
    favorite_count,
    retweet_count,
    engagement_count = favorite_count + retweet_count,
    status_url
  )

tweets_for_plot
tweets_for_plot2 <- tweets_for_plot %>% 
  mutate(engagement_count = ifelse(screen_name == "gabrielboric",
                                   - engagement_count,
                                   engagement_count))

breaks_values_eng <- pretty(tweets_for_plot2$engagement_count)

abs_k <- abs(breaks_values_eng)/1000

labels_k <- ifelse(abs_k == 0, "0", str_c(abs_k, "K"))
fig <- plot_ly(
  data = tweets_for_plot2,
  x = ~engagement_count,
  y = ~rank_engagement,
  text = ~abs(engagement_count),
  type = 'bar',
  color = ~screen_name,
  colors = c("red", "blue"),
  orientation = 'h',
      hovertemplate = paste0(
      "<b>%{text:,.0f}</b> likes and RTs | ",
      format(tweets_for_plot2$created_at, "%b %d"),
      " | @",
      tweets_for_plot2$screen_name,
      "<br><br>",
      str_wrap(tweets_for_plot2$text),
      "<extra></extra>"
      )
) %>% 
  layout(barmode = 'overlay',
         title = "Most popular tweets by Chilean presidential candidates",
         yaxis = list(title = "Ranking",
                      autorange="reversed",
                      showgrid=T,
                      autotick = F, tickmode = "array", tickvals = 1:10),
         xaxis = list(title = "Engagement (RTs + Likes)",
                      tickmode = 'array',
                      tickvals = breaks_values_eng,
                      ticktext = labels_k),
         legend = list(title=list(text='<b>Candidate</b>'),
                       orientation = "h",   # show entries horizontally
                       xanchor = "center",  # use center of legend as anchor
                       x = 0.5,
                       y=-0.2),
         hovermode = "closest",
         hoverlabel = list(bgcolor = "#DFDFDF", bordercolor = "black")
         )

# TODO: make them be in the same vertical position. ✅
# TODO: Fix the "Rank engagement" labels ✅
# TODO: Rename axis ✅
# TODO: Fix the horizontal axis ("Engagement") ✅
# TODO: Clean flyouts (change sign) ✅
# TODO: add twitter text as flyouts ✅ 
# TODO: change colours ✅

fig
LS0tDQp0aXRsZTogIlRvcCBUd2VldHMgZnJvbSBDaGlsZWFuIFByZXNpZGVudGlhbCBDYW5kaWRhdGVzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KVE9ETzoNCg0KLSAgIEZpZ3VyZSBvdXQgaG93IHRvIHN0b3JlIHNlY3JldHMgb3V0c2lkZSBvZiB0aGUgY29kZSDinIUNCi0gICBDb3B5IGNvZGUgZnJvbSBsYXN0IHNjcmlwdCDinIUNCi0gICBDb252ZXJ0IG91dHB1dCB0byBwbG90bHkg4pyFDQotICAgQWRkIGB4ZnVuOjpjYWNoZV9yZHNgIHRvIGNhY2hlIHR3ZWV0cyBhbmQgbm90IHdhc3RlIEFQSSBjYWxscyDinIUNCi0gICBNYWtlIHR3ZWV0cyBhdmFpbGFibGUgYXMgZmx5LW91dHMg4pyFDQotICAgVHJ5IHRvIGxvYWQgdGhlIHR3ZWV0IGluIGEgYm94IChjcm9zc3RhbGspDQotICAgU2VudGltZW50IGFuYWx5c2lzIG9mIHRoZSByZXBsaWVzIChjcm9zc3RhbGspDQoNCmBgYHtyfQ0KbGlicmFyeShydHdlZXQpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa2V5cmluZykNCmxpYnJhcnkocGxvdGx5KQ0KDQojIHR3aXR0ZXJfdG9rZW4gPC0gY3JlYXRlX3Rva2VuKA0KIyAgIGFwcCA9ICJwYWN0by1zb2NpYWwtMSIsDQojICAgY29uc3VtZXJfa2V5ID0gDQojICAgICBrZXlyaW5nOjprZXlfZ2V0KCJwYWN0by1zb2NpYWwtMS1jb25zdW1lcl9rZXkiKSwNCiMgICBjb25zdW1lcl9zZWNyZXQgPSANCiMgICAgIGtleXJpbmc6OmtleV9nZXQoInBhY3RvLXNvY2lhbC0xLWNvbnN1bWVyX3NlY3JldCIpLA0KIyAgIGFjY2Vzc190b2tlbiA9IA0KIyAgICAga2V5cmluZzo6a2V5X2dldCgicGFjdG8tc29jaWFsLTEtYWNjZXNzX3Rva2VuIiksDQojICAgYWNjZXNzX3NlY3JldCA9DQojICAgICBrZXlyaW5nOjprZXlfZ2V0KCJwYWN0by1zb2NpYWwtMS1hY2Nlc3Nfc2VjcmV0IiksDQojICAgc2V0X3JlbnYgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZXRyaWV2aW5nIHR3ZWV0cyBmcm9tIEdhYnJpZWwgQm9yaWMNCnR3ZWV0c19ieV9ib3JpYyA8LQ0KICB4ZnVuOjpjYWNoZV9yZHMoew0KICAgIGdldF90aW1lbGluZSgiQGdhYnJpZWxib3JpYyIsIG4gPSAzMjAwKQ0KICB9LA0KICBmaWxlID0gInR3ZWV0c19ieV9ib3JpYy5yZHMiKQ0KDQojIFJldHJpZXZpbmcgdHdlZXRzIGZyb20gSm9zZSBBbnRvbmlvIEthc3QNCnR3ZWV0c19ieV9rYXN0IDwtIHhmdW46OmNhY2hlX3Jkcyh7DQogIGdldF90aW1lbGluZSgiQGpvc2VhbnRvbmlva2FzdCIsIG4gPSAzMjAwKQ0KICB9LA0KICBmaWxlID0gInR3ZWV0c19ieV9rYXN0LnJkcyIpDQpgYGANCg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGZpbHRlciBvcmdhbmljIHR3ZWV0cyBhbmQgcmFuayB0aGVtIGJ5IGVuZ2FnZW1lbnQNCmdldF9vcmdhbmljX3JhbmtlZCA8LSBmdW5jdGlvbihkZikgew0KICBkZiAlPiUNCiAgICBmaWx0ZXIoaXNfcmV0d2VldCA9PSBGQUxTRSwNCiAgICAgICAgICAgaXMubmEocmVwbHlfdG9fc3RhdHVzX2lkKSwNCiAgICAgICAgICAgIyBvbmx5IHR3ZWV0cyBwcmV2aW91cyB0byB0aGUgcHJlc2lkZW50aWFsIGVsZWN0aW9uDQogICAgICAgICAgIGNyZWF0ZWRfYXQgPD0gbHVicmlkYXRlOjp5bWQoMjAyMTEyMjApKSAlPiUNCiAgICBtdXRhdGUocmFua19lbmdhZ2VtZW50ID0gZGVuc2VfcmFuayhkZXNjKGZhdm9yaXRlX2NvdW50ICsgcmV0d2VldF9jb3VudCkpKSAlPiUNCiAgICBhcnJhbmdlKHJhbmtfZW5nYWdlbWVudCkNCn0NCmBgYA0KDQpUaGVuIEkgYXBwbHkgdGhlIGZ1bmN0aW9uIHRvIGJvdGggZGF0YWZyYW1lcyBhbmQgbWVyZ2UgdGhlIHJlc3VsdHMgd2l0aCBtYXBfZGYuIE5leHQsIEkgZmlsdGVyIHRoZSByZXN1bHQgdG8ga2VlcCBvbmx5IHRoZSAxMCBtb3JlIHBvcHVsYXIgdHdlZXRzIG9mIGVhY2ggY2FuZGlkYXRlLCBhbmQgc2VsZWN0IHRoZSByZWxldmFudCBjb2x1bW5zIGZvciB0aGUgdmlzdWFsaXphdGlvbi4NCg0KYGBge3J9DQp0d2VldHNfZm9yX3Bsb3QgPC0NCiAgbGlzdCh0d2VldHNfYnlfYm9yaWMsDQogICAgICAgdHdlZXRzX2J5X2thc3QpICU+JQ0KICBtYXBfZGYoZ2V0X29yZ2FuaWNfcmFua2VkKSAlPiUNCiAgZmlsdGVyKHJhbmtfZW5nYWdlbWVudCA8PSAxMCkgJT4lDQogIHRyYW5zbXV0ZSgNCiAgICByYW5rX2VuZ2FnZW1lbnQsDQogICAgc2NyZWVuX25hbWUsDQogICAgc3RhdHVzX2lkLA0KICAgIGNyZWF0ZWRfYXQsDQogICAgdGV4dCwNCiAgICBmYXZvcml0ZV9jb3VudCwNCiAgICByZXR3ZWV0X2NvdW50LA0KICAgIGVuZ2FnZW1lbnRfY291bnQgPSBmYXZvcml0ZV9jb3VudCArIHJldHdlZXRfY291bnQsDQogICAgc3RhdHVzX3VybA0KICApDQoNCnR3ZWV0c19mb3JfcGxvdA0KYGBgDQoNCmBgYHtyfQ0KdHdlZXRzX2Zvcl9wbG90MiA8LSB0d2VldHNfZm9yX3Bsb3QgJT4lIA0KICBtdXRhdGUoZW5nYWdlbWVudF9jb3VudCA9IGlmZWxzZShzY3JlZW5fbmFtZSA9PSAiZ2FicmllbGJvcmljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLSBlbmdhZ2VtZW50X2NvdW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmdhZ2VtZW50X2NvdW50KSkNCg0KYnJlYWtzX3ZhbHVlc19lbmcgPC0gcHJldHR5KHR3ZWV0c19mb3JfcGxvdDIkZW5nYWdlbWVudF9jb3VudCkNCg0KYWJzX2sgPC0gYWJzKGJyZWFrc192YWx1ZXNfZW5nKS8xMDAwDQoNCmxhYmVsc19rIDwtIGlmZWxzZShhYnNfayA9PSAwLCAiMCIsIHN0cl9jKGFic19rLCAiSyIpKQ0KYGBgDQoNCmBgYHtyfQ0KZmlnIDwtIHBsb3RfbHkoDQogIGRhdGEgPSB0d2VldHNfZm9yX3Bsb3QyLA0KICB4ID0gfmVuZ2FnZW1lbnRfY291bnQsDQogIHkgPSB+cmFua19lbmdhZ2VtZW50LA0KICB0ZXh0ID0gfmFicyhlbmdhZ2VtZW50X2NvdW50KSwNCiAgdHlwZSA9ICdiYXInLA0KICBjb2xvciA9IH5zY3JlZW5fbmFtZSwNCiAgY29sb3JzID0gYygicmVkIiwgImJsdWUiKSwNCiAgb3JpZW50YXRpb24gPSAnaCcsDQogICAgICBob3ZlcnRlbXBsYXRlID0gcGFzdGUwKA0KICAgICAgIjxiPiV7dGV4dDosLjBmfTwvYj4gbGlrZXMgYW5kIFJUcyB8ICIsDQogICAgICBmb3JtYXQodHdlZXRzX2Zvcl9wbG90MiRjcmVhdGVkX2F0LCAiJWIgJWQiKSwNCiAgICAgICIgfCBAIiwNCiAgICAgIHR3ZWV0c19mb3JfcGxvdDIkc2NyZWVuX25hbWUsDQogICAgICAiPGJyPjxicj4iLA0KICAgICAgc3RyX3dyYXAodHdlZXRzX2Zvcl9wbG90MiR0ZXh0KSwNCiAgICAgICI8ZXh0cmE+PC9leHRyYT4iDQogICAgICApDQopICU+JSANCiAgbGF5b3V0KGJhcm1vZGUgPSAnb3ZlcmxheScsDQogICAgICAgICB0aXRsZSA9ICJNb3N0IHBvcHVsYXIgdHdlZXRzIGJ5IENoaWxlYW4gcHJlc2lkZW50aWFsIGNhbmRpZGF0ZXMiLA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlJhbmtpbmciLA0KICAgICAgICAgICAgICAgICAgICAgIGF1dG9yYW5nZT0icmV2ZXJzZWQiLA0KICAgICAgICAgICAgICAgICAgICAgIHNob3dncmlkPVQsDQogICAgICAgICAgICAgICAgICAgICAgYXV0b3RpY2sgPSBGLCB0aWNrbW9kZSA9ICJhcnJheSIsIHRpY2t2YWxzID0gMToxMCksDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiRW5nYWdlbWVudCAoUlRzICsgTGlrZXMpIiwNCiAgICAgICAgICAgICAgICAgICAgICB0aWNrbW9kZSA9ICdhcnJheScsDQogICAgICAgICAgICAgICAgICAgICAgdGlja3ZhbHMgPSBicmVha3NfdmFsdWVzX2VuZywNCiAgICAgICAgICAgICAgICAgICAgICB0aWNrdGV4dCA9IGxhYmVsc19rKSwNCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QodGl0bGU9bGlzdCh0ZXh0PSc8Yj5DYW5kaWRhdGU8L2I+JyksDQogICAgICAgICAgICAgICAgICAgICAgIG9yaWVudGF0aW9uID0gImgiLCAgICMgc2hvdyBlbnRyaWVzIGhvcml6b250YWxseQ0KICAgICAgICAgICAgICAgICAgICAgICB4YW5jaG9yID0gImNlbnRlciIsICAjIHVzZSBjZW50ZXIgb2YgbGVnZW5kIGFzIGFuY2hvcg0KICAgICAgICAgICAgICAgICAgICAgICB4ID0gMC41LA0KICAgICAgICAgICAgICAgICAgICAgICB5PS0wLjIpLA0KICAgICAgICAgaG92ZXJtb2RlID0gImNsb3Nlc3QiLA0KICAgICAgICAgaG92ZXJsYWJlbCA9IGxpc3QoYmdjb2xvciA9ICIjREZERkRGIiwgYm9yZGVyY29sb3IgPSAiYmxhY2siKQ0KICAgICAgICAgKQ0KDQojIFRPRE86IG1ha2UgdGhlbSBiZSBpbiB0aGUgc2FtZSB2ZXJ0aWNhbCBwb3NpdGlvbi4g4pyFDQojIFRPRE86IEZpeCB0aGUgIlJhbmsgZW5nYWdlbWVudCIgbGFiZWxzIOKchQ0KIyBUT0RPOiBSZW5hbWUgYXhpcyDinIUNCiMgVE9ETzogRml4IHRoZSBob3Jpem9udGFsIGF4aXMgKCJFbmdhZ2VtZW50Iikg4pyFDQojIFRPRE86IENsZWFuIGZseW91dHMgKGNoYW5nZSBzaWduKSDinIUNCiMgVE9ETzogYWRkIHR3aXR0ZXIgdGV4dCBhcyBmbHlvdXRzIOKchSANCiMgVE9ETzogY2hhbmdlIGNvbG91cnMg4pyFDQoNCmZpZw0KYGBgDQoNCiMjIE5leHQNCg0KTkVYVDogY3Jvc3N0YWxrLg0KaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9jcm9zc3RhbGsvaW5kZXguaHRtbCANCg0KVGFza3M6DQpb4pyFXSBGaWx0ZXIgdHdlZXRzIGFmdGVyIHRoZSBwcmVzaWRlbnRpYWwgZWxlY3Rpb24NClsgXSBSZXRyaWV2ZSB0aGUgcmVwbGllcyBvZiB0aGVzZSB0d2VldHMNCiAgICBQb3NzaWJsZSBhcHByb2FjaDoNCiAgICAtIEdldCBhbGwgdGhlIGZvbGxvd2VycyBvZiBlYWNoIG9uZQ0KICAgIC0gTG9vcCB0aHJvdWdoIHRoZW0gYW5kIGdldCB0aGVpciB0aW1lbGluZXMNCiAgICAtIEZpbHRlciBieSAiaW4gcmVwbHkgdG8iIHVzaW5nIHRoZSBJRHMgb2YgdGhlIHR3ZWV0cyBJIHdhbnQNClsgXSBTZXQgdXAgYSBtaW5pbWFsIGNyb3NzdGFsayBleGFtcGxlIA0KDQo=